#include "Renderer.h"
#include <iostream>

int main()
{
    using namespace Nitro;
    glfwInit();

    Renderer::OpenGLConfig openglDesc;
    openglDesc.samples = 16;
    openglDesc.versionMajor = 4;
    openglDesc.versionMinor = 5;
    Renderer::Init(openglDesc);

    auto window = glfwCreateWindow(600, 600, APP_NAME, 0, 0);
    glfwMakeContextCurrent(window);
    gladLoadGLLoader(GLADloadproc(glfwGetProcAddress));

    Renderer::ShaderDescriptor shaderDesc;
    shaderDesc.type = Renderer::VertexShader;
    shaderDesc.sourceType = Renderer::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    layout (location = 0) in vec2 pos;
    layout (location = 1) in vec3 vertColor;
    uniform mat4 transform;
    out vec3 color;
    void main() {
        gl_Position = transform * vec4(pos, 0.0f, 1.0f);
        color = vertColor;
    }
    )delimiter";
    auto vertexShader = Renderer::CreateShader(shaderDesc);
    if (vertexShader.hasReport())
    {
        std::cout << vertexShader.getReport();
    }
    shaderDesc.type = Renderer::FragmentShader;
    shaderDesc.sourceType = Renderer::SourceCode;
    shaderDesc.source = R"delimiter(
    #version 450 core
    in vec3 color;
    out vec4 fragColor;
    void main() {
        fragColor = vec4(color, 1.0f);
    }
    )delimiter";
    auto fragmentShader = Renderer::CreateShader(shaderDesc);
    if (fragmentShader.hasReport())
    {
        std::cout << fragmentShader.getReport();
    }

    Renderer::PipelineDescriptor pipelineDesc;
    pipelineDesc.vertexShader = vertexShader;
    pipelineDesc.fragmentShader = fragmentShader;
    pipelineDesc.drawMode = Renderer::TriangleFan;
    auto pipeline = Renderer::CreatePipeline(pipelineDesc);
    if (pipeline.hasReport())
    {
        std::cout << pipeline.getReport();
    }

    float verts[] = {
        0.5f,  0.5f,  1.0f, 0.0f, 0.0f, //
        0.5f,  -0.5f, 0.0f, 1.0f, 0.0f, //
        -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, //
        -0.5f, 0.5f,  0.0f, 1.0f, 1.0f, //
    };

    Renderer::BufferDescriptor vertexBufferDesc;
    vertexBufferDesc.bindFlags = Renderer::VertexBuffer;
    vertexBufferDesc.size = sizeof(verts);
    vertexBufferDesc.vertexAttribs.push_back(
        {0, 0, 5 * sizeof(float), Renderer::RG32Float});
    vertexBufferDesc.vertexAttribs.push_back(
        {1, 2 * sizeof(float), 5 * sizeof(float), Renderer::RGB32Float});
    Renderer::Buffer vertexBuffer =
        Renderer::CreateBuffer(vertexBufferDesc, verts);

    glfwSetWindowSizeCallback(
        window, [](auto, int w, int h) { glViewport(0, 0, w, h); });

    pipeline.activate();
    pipeline.setVertexBuffer(vertexBuffer);

    glm::mat4 transform(1.0f);
    transform = glm::translate(transform, {0.0f, 0.0f, -1.0f});
    glm::mat4 projection = glm::perspective(60.0f, 1.0f, 0.01f, 100.0f);
    double prevTime, currentTime;
    prevTime = currentTime = glfwGetTime();

    while (!glfwWindowShouldClose(window))
    {
        Renderer::Clear();
        Renderer::ClearColor();

        currentTime = glfwGetTime();

        transform = glm::translate(
            transform, {0.0f, 0.0f, float(currentTime - prevTime) * 0.1f});
        transform = glm::rotate(transform, float(currentTime - prevTime),
                                {0.0f, 0.0f, 1.0f});
        pipeline.setUniform("transform", projection * transform);

        pipeline.draw(0, 4);

        prevTime = currentTime;

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}